﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using Babylon.Toolbox;
using XnaTools;
using System.Windows.Controls;
using SStuff.XnaInterop;
using System.IO;
using Microsoft.Xna.Framework.Silverlight;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace TankSample
{
    public class Scene : BaseScene
    {
        public Scene(DrawingSurface drawingSurface, FrameworkElement inputSurface) : base(drawingSurface, inputSurface)
        {
            _camera = new OrbitCamera();
            _camera.FarPlane = 10000;
            _camera.NearPlane = 0.1f;
            _camera.Radius = 2000;
            _camera.AttachTo(inputSurface);
            _lightDir0.Normalize();
            _lightDir1.Normalize();
            _lightDir2.Normalize();
        }


        #region fields
        OrbitCamera _camera;
        InteropModel _tankModel;
        private Model _babyTank;


        private float _specularPower = 10;

        public float SpecularPower
        {
            get { return _specularPower; }
            set { _specularPower = value; }
        }

        private float _specularAmount = 0.5f;

        public float SpecularAmount
        {
            get { return _specularAmount; }
            set { _specularAmount = value; }
        }

        private Color _specularColor = new Color(255, 240, 190, 255);

        public Color SpecularColor
        {
            get { return _specularColor; }
            set { _specularColor = value; }
        }

        private Vector3 _lightDir0 = new Vector3(1.4f,-1,-0.7f);

        public Vector3 LightDir0
        {
            get { return _lightDir0; }
            set { _lightDir0 = value; }
        }

        private Vector3 _lightDir1 = new Vector3(-1.4f, -1, -0.7f);

        public Vector3 LightDir1
        {
            get { return _lightDir1; }
            set { _lightDir1 = value; }
        }

        private Vector3 _lightDir2 = new Vector3(0, -1, 0.2f);

        public Vector3 LightDir2
        {
            get { return _lightDir2; }
            set { _lightDir2 = value; }
        }


        Matrix[] _bonesTransforms;
        Matrix _lBackWheelTransform,
              _rBackWheelTransform,
              _lFrontWheelTransform,
              _rFrontWheelTransform,
              _lSteerTransform,
              _rSteerTransform,
              _turretTransform,
              _canonTransform,
              _hatchTransform;

        float _width, _height;
        #endregion

        

        protected override void OnLoaded(object sender, RoutedEventArgs e)
        {
            var gd = GraphicsDeviceManager.Current.GraphicsDevice;
            using (var tankStream = Application.GetResourceStream(new Uri("Models/tank.bin", UriKind.Relative)).Stream)
            using(var reader = new BinaryReader(tankStream))
            {
                _tankModel = InteropModel.FromBinaryReader(reader);
                _tankModel.Prepare(gd);
            }
            _babyTank = _tankModel.ToBabylonModel(gd, (m, p)=>{
                var effect = new BasicEffect3Lights(gd);
                effect.SceneAmbientColor = new Color(0.1f,0.1f,0.1f,1.0f);
                effect.AmbientColor = new Color(0.1f, 0.1f, 0.1f, 1);
                effect.SpecularPower = p.EffectParameters.Where(pa=>pa.ParameterName == "SpecularPower").First().Data[0].X;
                var spcularColor = p.EffectParameters.Where(pa => pa.ParameterName == "SpecularColor").First().Data[0] ;
                effect.SpecularColor = new Color(spcularColor.X, spcularColor.Y, spcularColor.Z,0);
                effect.DiffuseTexture = p.IndexedTextureUsages["Texture"].SL5Texture;
                var diffuse = p.EffectParameters.Where(pa => pa.ParameterName == "DiffuseColor").First().Data[0];
                effect.DiffuseColor = new Color(diffuse.X, diffuse.Y, diffuse.Z, diffuse.W);
                var emissive = p.EffectParameters.Where(pa => pa.ParameterName == "EmissiveColor").First().Data[0];
                effect.EmissiveColor = new Color(emissive.X, emissive.Y, emissive.Z, 1);
                effect.Shininess = 0.6f;
                return effect;
            });

            // extracting bones transforms for animation
            _bonesTransforms = new Matrix[_tankModel.Bones.Length];

            var lBackWheelBone = _babyTank.IndexedBones["l_back_wheel_geo"];
            var rBackWheelBone = _babyTank.IndexedBones["r_back_wheel_geo"];
            var lFrontWheelBone = _babyTank.IndexedBones["l_front_wheel_geo"];
            var rFrontWheelBone = _babyTank.IndexedBones["r_front_wheel_geo"];
            var lSteerBone = _babyTank.IndexedBones["l_steer_geo"];
            var rSteerBone = _babyTank.IndexedBones["r_steer_geo"];
            var turretBone = _babyTank.IndexedBones["turret_geo"];
            var canonBone = _babyTank.IndexedBones["canon_geo"];
            var hatchBone = _babyTank.IndexedBones["hatch_geo"];

            _lBackWheelTransform = lBackWheelBone.Transform;
            _rBackWheelTransform = rBackWheelBone.Transform;
            _lFrontWheelTransform = lFrontWheelBone.Transform;
            _rFrontWheelTransform = rFrontWheelBone.Transform;
            _lSteerTransform = lSteerBone.Transform;
            _rSteerTransform = rSteerBone.Transform;
            _turretTransform = turretBone.Transform;
            _canonTransform = canonBone.Transform;
            _hatchTransform = hatchBone.Transform;


        }
        protected override void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            _width = (float)e.NewSize.Width;
            _height = (float)e.NewSize.Height;
            _camera.AspectRatio = _width / _height;
        }

        private void AnimateModelAndComputeAbsoluteBones(double time)
        {
            var steerRotationAmount = (float)Math.Sin(time * 0.75f) * 0.5f;
            var wheelRotationAmount = (float)time * 3;
            var turretRotationAmount = (float)Math.Sin(time * 0.333f) * 1.25f;
            var cannonRotationAmount = (float)Math.Sin(time * 0.25f) * 0.333f - 0.333f;
            var hatchRotationAmount = MathHelper.Clamp((float)Math.Sin(time * 2) * 2, -1, 0);

            Matrix wheelRotation = Matrix.CreateRotationX(wheelRotationAmount);
            Matrix steerRotation = Matrix.CreateRotationY(steerRotationAmount);
            Matrix turretRotation = Matrix.CreateRotationY(turretRotationAmount);
            Matrix cannonRotation = Matrix.CreateRotationX(cannonRotationAmount);
            Matrix hatchRotation = Matrix.CreateRotationX(hatchRotationAmount);


            _tankModel.IndexedBones["l_back_wheel_geo"].Transform = wheelRotation * _lBackWheelTransform;
            _tankModel.IndexedBones["r_back_wheel_geo"].Transform = wheelRotation * _rBackWheelTransform;
            _tankModel.IndexedBones["l_front_wheel_geo"].Transform = wheelRotation * _lFrontWheelTransform;
            _tankModel.IndexedBones["r_front_wheel_geo"].Transform = wheelRotation * _rFrontWheelTransform;
            _tankModel.IndexedBones["l_steer_geo"].Transform = Matrix.Multiply(steerRotation, _lSteerTransform);
            _tankModel.IndexedBones["r_steer_geo"].Transform = Matrix.Multiply(steerRotation, _rSteerTransform);
            _tankModel.IndexedBones["turret_geo"].Transform = Matrix.Multiply(turretRotation, _turretTransform);
            _tankModel.IndexedBones["canon_geo"].Transform = Matrix.Multiply(cannonRotation, _canonTransform);
            _tankModel.IndexedBones["hatch_geo"].Transform = Matrix.Multiply(hatchRotation, _hatchTransform);

            _tankModel.CopyAbsoluteBonesTo(_bonesTransforms);
        }

        protected override void OnDraw(object sender, System.Windows.Controls.DrawEventArgs e)
        {
            var gd = e.GraphicsDevice;
            e.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Color(50, 100, 200, 255), 1, 0);
            Matrix world = Matrix.Identity;

            AnimateModelAndComputeAbsoluteBones(e.TotalTime.TotalSeconds);

            _babyTank.Device = gd;

            foreach (var mesh in _babyTank.Meshes)
            {
                foreach (var effect in mesh.Effects){
                    var ef = effect as BasicEffect3Lights;
                    ef.Device = gd;
                    ef.CameraPosition = _camera.Position;
                    ef.LightDir0 = _lightDir0;
                    ef.LightDir1 = LightDir1;
                    ef.LightDir2 = LightDir2;
                    ef.SpecularPower = _specularPower;
                    ef.SpecularColor = new Color(_specularColor.R, _specularColor.G, _specularColor.B, (byte) (255 * _specularAmount));
                    ef.World = _bonesTransforms[mesh.BoneIndex];
                    ef.View = _camera.View;
                    ef.Projection = _camera.Projection;
                }
                
            }
            _babyTank.Draw();
            _camera.ApplyInertia();
            e.InvalidateSurface();
        }

       
    }
}
